package com.lagou.executor.impl;

import com.lagou.executor.Executor;
import com.lagou.pojo.BoundSql;
import com.lagou.pojo.Configuration;
import com.lagou.pojo.MappedStatement;
import com.lagou.util.GenericTokenParser;
import com.lagou.util.ParameterMapping;
import com.lagou.util.ParameterMappingTokenHandler;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class SimpleExecutor implements Executor {

    @Override
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object[] params) throws Exception {
        // 1. 注册驱动，获取连接
        Connection connection = configuration.getDataSource().getConnection();

        // 2. 获取sql语句
        String sql = mappedStatement.getSql();
        BoundSql boundSql = getBoundSql(sql);
        String log = "[executor do query] sql=" + sql;

        // 3.获取预处理对象：preparedStatement
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());

        // 4. 设置参数
        Class<?> parameterTypeClass = mappedStatement.getParameterType();
        if (parameterTypeClass != null && !Void.class.equals(parameterTypeClass)) {
            log += ", paramValues=";
            List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
            for (int i = 0; i < parameterMappingList.size(); i++) {
                ParameterMapping pm = parameterMappingList.get(i);
                Object paramValue = reflectGetObjectFieldValue(parameterTypeClass, params[0], pm.getContent());
                preparedStatement.setObject(i + 1, paramValue);
                log += "[" + paramValue + "]";
            }
        }

        // 5. 执行sql
        ResultSet resultSet = preparedStatement.executeQuery();
        System.out.println(log);

        // 6. 封装返回结果集
        Class<?> resultTypeClass = mappedStatement.getResultType();
        if (resultTypeClass != null && !Void.class.equals(resultTypeClass)) {
            ArrayList<Object> objects = new ArrayList<>();
            while (resultSet.next()) {
                Object o = resultTypeClass.newInstance();
                ResultSetMetaData metaData = resultSet.getMetaData();
                for (int i = 1; i <= metaData.getColumnCount(); i++) {
                    String columnName = metaData.getColumnName(i);
                    Object value = resultSet.getObject(columnName);
                    PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass);
                    Method writeMethod = propertyDescriptor.getWriteMethod();
                    writeMethod.invoke(o, value);
                }
                objects.add(o);
            }
            return (List<E>) objects;
        }

        return null;
    }

    /**
     * 解析sql占位符，生成参数映射关系列表
     *
     * @param sql
     * @return
     */
    private BoundSql getBoundSql(String sql) {
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
        String parsedSqlText = genericTokenParser.parse(sql);
        List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();
        BoundSql boundSql = new BoundSql(parsedSqlText, parameterMappings);
        return boundSql;
    }


    @Override
    public <T> int update(Configuration configuration, MappedStatement mappedStatement, Object[] param) throws Exception {
        // 1. 注册驱动，获取连接
        Connection connection = configuration.getDataSource().getConnection();

        // 2. 获取sql语句
        String sql = mappedStatement.getSql();
        BoundSql boundSql = getBoundSql(sql);
        String log = "[executor do update] sql=" + sql;

        // 3.获取预处理对象：preparedStatement
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());

        // 4. 设置参数
        Class<?> parameterTypeClass = mappedStatement.getParameterType();
        if (parameterTypeClass == null || Void.class.equals(parameterTypeClass)) {
            throw new RuntimeException("增删改操作必须指定入参类型");
        }
        List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
        for (int i = 0; i < parameterMappingList.size(); i++) {
            ParameterMapping pm = parameterMappingList.get(i);
            Object paramValue = reflectGetObjectFieldValue(parameterTypeClass, param[0], pm.getContent());
            preparedStatement.setObject(i + 1, paramValue);
        }

        // 5. 执行sql
        int rows = preparedStatement.executeUpdate();
        System.out.println(log);

        return rows;
    }

    /**
     * 反射获取对象属性值
     *
     * @param clazz
     * @param object
     * @param field
     * @return
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    private Object reflectGetObjectFieldValue(Class clazz, Object object, String field)
            throws NoSuchFieldException, IllegalAccessException {
        Field declaredField = clazz.getDeclaredField(field);
        declaredField.setAccessible(true);
        return declaredField.get(object);
    }
}
